移动架构 (八) 动态化加载插件技术模型实现

您所在的位置:网站首页 历史小说 知乎免费阅读 移动架构 (八) 动态化加载插件技术模型实现

移动架构 (八) 动态化加载插件技术模型实现

2023-04-02 11:59| 来源: 网络整理| 查看: 265

基本概念

插件化其实也就是 模块化->组件化 演变而来, 属于动态加载技术,主要用于解决应用越来越庞大以及功能模块的解耦,小项目中一般用的不多。

原理: 插件化的原理其实就是在 APP 壳运行过程中,动态加载一些程序中原本不存在的可执行文件并运行这些文件中的代码逻辑。可执行文件总的来说分为两个,其一是动态链接库 so,其二是 dex 相关文件包括 jar/apk 文件。

发展历史

很早以前插件化这项技术已经有公司在研究了,淘宝,支付宝做的是比较早,但是淘宝这项技术一直都是保密的,直到 2015 年左右市面上才出现了一些关于插件化的框架,Android 插件化分为很多技术流派,实现的方式都不太一样。下面我就简单以时间线来举例几个比较有代表性的插件框架:

时间

框架名称

作者

框架简介

2014年底

dynamic-load-apk

任玉刚

动态加载技术 + 代理实现

2015年 8 月

DroidPlugin

360 手机助手

可以直接运行第三方的独立 APK 文件,完全不需要对 APK 进行修改或安装。一种新的插件机制,一种免安装的运行机制,是一个沙箱(但是不完全的沙箱。就是对于使用者来说,并不知道他会把 apk 怎么样), 是模块化的基础。

2015年底

Small

wequick

Small 是一种实现轻巧的跨平台插件化框架,基于“轻量、透明、极小化、跨平台”的理念

2017年 6 月

VirtualAPK

滴滴

VirtualAPK 对插件没有额外的约束,原生的 apk 即可作为插件。插件工程编译生成 apk 后,即可通过宿主 App 加载,每个插件 apk 被加载后,都会在宿主中创建一个单独的 LoadedPlugin 对象。通过这些 LoadedPlugin 对象,VirtualAPK 就可以管理插件并赋予插件新的意义,使其可以像手机中安装过的 App 一样运行。

2017年 7 月

RePlgin

360手机卫士

RePlugin 是一套完整的、稳定的、适合全面使用的,占坑类插件化方案,由 360 手机卫士的RePlugin Team 研发,也是业内首个提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。

2019

Shadow

腾讯

Shadow 是一个腾讯自主研发的 Android 插件框架,经过线上亿级用户量检验。 Shadow 不仅开源分享了插件技术的关键代码,还完整的分享了上线部署所需要的所有设计(零反射)

插件化必备知识

Binder

APP 打包流程

APP 安装流程

APP 启动流程

资源加载机制

反射,ClassLoader

...

实现简易版本插件化框架

今天我们这里就以动态加载技术,ClassLoader + 反射 + 代理模式 等基本技术来实现动态加载 APK 中的(Activity, Broadcast, Service ,资源)项目地址

先来看一个我们最终实现的效果

加载插件 APK

在加载 APK 之前我们先来了解下 ClassLoader 家族,继承关系图

DexClassLoader 加载流程

从上面 2 张图中,我们得知动态加载 APK 需要用到 DexClassLoader ,既然知道了用 DexClassLoader 来加载 APK , 那么native 中将 apk -> dex 解析出来,class 又怎么加载勒? 通过 DexClassLoader 流程图得知可以直接调用 loadClass(String classPath) 来加载,下面我们就正式进行今天的主题了。

代码实现加载 APK

    /** * 加载插件 APK */ public boolean loadPlugin(Context context, String filePath) { if (context == null || filePath == null || filePath.isEmpty()) throw new NullPointerException("context or filePath is null ?"); this.mContext = context.getApplicationContext(); this.apkFilePath = filePath; //拿到 包管理 packageManager = mContext.getPackageManager(); if (getPluginPackageInfo(apkFilePath) == null) { return false; } //从包里获取 Activity pluginPackageInfo = getPluginPackageInfo(apkFilePath); //存放 DEX 路径 mDexPath = new File(Constants.IPluginPath.PlugDexPath); if (mDexPath.exists()) mDexPath.delete(); else mDexPath.mkdirs(); //通过 DexClassLoader 加载 apk 并通过 native 层解析 apk 输出 dex //第二个参数可以为 null if (getPluginClassLoader(apkFilePath, mDexPath.getAbsolutePath()) == null || getPluginResources(filePath) == null) return false; this.mDexClassLoader = getPluginClassLoader(apkFilePath, mDexPath.getAbsolutePath()); this.mResources = getPluginResources(filePath); return true; } /** * @return 得到对应插件 APK 的 Resource 对象 */ public Resources getPluginResources() { return getPluginResources(apkFilePath); } /** * 得到对应插件 APK 中的 加载器 * * @param apkFile * @param dexPath * @return */ public DexClassLoader getPluginClassLoader(String apkFile, String dexPath) { return new DexClassLoader(apkFile, dexPath, null, mContext.getClassLoader()); } /** * 得到对应插件 APK 中的 加载器 * * @return */ public DexClassLoader getPluginClassLoader() { return getPluginClassLoader(apkFilePath, mDexPath.getAbsolutePath()); } /** * 得到插件 APK 中 包信息 */ public PackageInfo getPluginPackageInfo(String apkFilePath) { if (packageManager != null) return packageManager.getPackageArchiveInfo(apkFilePath, PackageManager.GET_ACTIVITIES); return null; } /** * 得到插件 APK 中 包信息 */ public PackageInfo getPluginPackageInfo() { return getPluginPackageInfo(apkFilePath); }

加载插件中 Activity

实现流程

代码实现流程

代理类 ProxyActivity 实现

public class ProxyActivity extends AppCompatActivity { /** * 需要加载插件的全类名 */ protected String activityClassName; private String TAG = this.getClass().getSimpleName(); private IActivity iActivity; private ProxyBroadcast receiver; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); activityClassName = getLoadClassName(); //拿到加载插件的的全类名 通过反射实例化 try { Class pluginClassName = getClassLoader().loadClass(activityClassName); //拿到构造函数 Constructor constructor = pluginClassName.getConstructor(new Class[]{}); //实例化 拿到插件 UI Object pluginObj = constructor.newInstance(new Object[]{}); if (pluginObj != null) { iActivity = (IActivity) pluginObj; iActivity.onActivityCreated(this, savedInstanceState); } } catch (Exception e) { Log.e(TAG, e.getMessage()); } } }

重写代理类中的 startActivity

    /** * 这里的 startActivity 是插件促使调用的 */ @Override public void startActivity(Intent intent) { //需要开启插件 Activity 的全类名 String className = getLoadClassName(intent); Intent proxyIntent = new Intent(this, ProxyActivity.class); proxyIntent.putExtra(Constants.ACTIVITY_CLASS_NAME, className); super.startActivity(proxyIntent); }

插件 Activity 实现 IActivity 的生命周期并且重写一些重要函数,都交于插件中处理

public class BaseActivityImp extends AppCompatActivity implements IActivity { private final String TAG = getClass().getSimpleName(); /** * 代理 Activity */ protected Activity that; @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle bundle) { this.that = activity; Log.i(TAG, " onActivityCreated"); onCreate(bundle); } /** * 通过 View 方式加载 * * @param view */ @Override public void setContentView(View view) { Log.i(TAG, " setContentView --> view"); if (that != null) { that.setContentView(view); } else { super.setContentView(view); } } /** * 通过 layoutID 加载 * * @param layoutResID */ @Override public void setContentView(int layoutResID) { Log.i(TAG, " setContentView --> layoutResID"); if (that != null) { that.setContentView(layoutResID); } else { super.setContentView(layoutResID); } } /** * 通过代理 去找布局 ID * * @param id * @param * @return */ @Override public T findViewById(int id) { if (that != null) return that.findViewById(id); return super.findViewById(id); } /** * 通过 代理去开启 Activity * * @param intent */ @Override public void startActivity(Intent intent) { if (that != null) { Intent tempIntent = new Intent(); tempIntent.putExtra(Constants.ACTIVITY_CLASS_NAME, intent.getComponent().getClassName()); that.startActivity(tempIntent); } else super.startActivity(intent); } @Override public String getPackageName() { return that.getPackageName(); } @Override public void onActivityStarted(@NonNull Activity activity) { Log.i(TAG, " onActivityStarted"); onStart(); } @Override public void onActivityResumed(@NonNull Activity activity) { Log.i(TAG, " onActivityResumed"); onResume(); } @Override public void onActivityPaused(@NonNull Activity activity) { Log.i(TAG, " onActivityPaused"); onPause(); } @Override public void onActivityStopped(@NonNull Activity activity) { Log.i(TAG, " onActivityStopped"); onStop(); } @Override public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle bundle) { onSaveInstanceState(bundle); Log.i(TAG, " onActivitySaveInstanceState"); } @Override public void onActivityDestroyed(@NonNull Activity activity) { Log.i(TAG, " onActivityDestroyed"); onDestroy(); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { } @Override protected void onStart() { } @Override protected void onResume() { } @Override protected void onStop() { } @Override protected void onPause() { } @Override protected void onSaveInstanceState(Bundle outState) { } @Override protected void onDestroy() { } @Override public void onBackPressed() { } }

加载插件中 Broadcast

流程图

代码实现

代理 ProxyActivity 中重写注册广播

    @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { IntentFilter proxyIntentFilter = new IntentFilter(); for (int i = 0; i < filter.countActions(); i++) { //内部是一个数组 proxyIntentFilter.addAction(filter.getAction(i)); } //交给代理广播去注册 this.receiver = new ProxyBroadcast(receiver.getClass().getName(), this); return super.registerReceiver(this.receiver, filter); }

加载插件中需要注册的广播全路径

public ProxyBroadcast(String broadcastClassName, Context context) { this.broadcastClassName = broadcastClassName; this.iBroadcast = iBroadcast; //通过加载插件的 DexClassLoader loadClass try { Class pluginBroadcastClassName = PluginManager.getInstance().getPluginClassLoader().loadClass(broadcastClassName); Constructor constructor = pluginBroadcastClassName.getConstructor(new Class[]{}); iBroadcast = (IBroadcast) constructor.newInstance(new Object[]{}); //返回给插件中广播生命周期 iBroadcast.attach(context); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, e.getMessage()); } }

接收到消息返回给插件中

   @Override public void onReceive(Context context, Intent intent) { iBroadcast.onReceive(context, intent); }

插件中广播注册

    /** * 动态注册广播 */ public void register() { //动态注册广播 IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("_DevYK"); receiver = new PluginBroadReceiver(); registerReceiver(receiver, intentFilter); } /** * 通过代理去注册广播 * * @param receiver * @param filter * @return */ @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { if (that != null) { return that.registerReceiver(receiver, filter); } else return super.registerReceiver(receiver, filter); }

插件中实现代理广播中的生命周期并实现接收函数

public class BaseBroadReceiverImp extends BroadcastReceiver implements IBroadcast { //代理广播中绑定成功插件广播 @Override public void attach(Context context) { } //代理广播接收到数据转发给插件中 @Override public void onReceive(Context context, Intent intent) { } }

加载插件中 Service

流程图

代码实现

ProxyAcitivy 开启插件中服务

    /** * 加载插件中 启动服务 * @param service * @return */ @Override public ComponentName startService(Intent service) { String className = getLoadServiceClassName(service); Intent intent = new Intent(this,ProxyService.class); intent.putExtra(Constants.SERVICE_CLASS_NAME,className); return super.startService(intent); }

ProxyService.java

public class ProxyService extends Service { private IService iService; @Override public IBinder onBind(Intent intent) { return iService.onBind(intent); } @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (iService == null) init(intent); return iService.onStartCommand(intent, flags, startId); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); iService.onStart(intent,startId); } @Override public boolean onUnbind(Intent intent) { iService.onUnbind(intent); return super.onUnbind(intent); } @Override public void onDestroy() { super.onDestroy(); iService.onDestroy(); } //初始化 public void init(Intent proIntent) { //拿到需要启动服务的全类名 String serviceClassName = getServiceClassName(proIntent); try { Class pluginService = PluginManager.getInstance().getPluginClassLoader().loadClass(serviceClassName); Constructor constructor = pluginService.getConstructor(new Class[]{}); iService = (IService) constructor.newInstance(new Object[]{}); iService.onCreate(getApplicationContext()); } catch (Exception e) { //加载 class } } @Override public ClassLoader getClassLoader() { return PluginManager.getInstance().getPluginClassLoader(); } public String getServiceClassName(Intent intent) { return intent.getStringExtra(Constants.SERVICE_CLASS_NAME); } }

插件服务实现 IService

public class BaseServiceImp extends Service implements IService { ... }

插件中重写 startService 交于代理中处理

    /** * 加载插件中服务,交于代理处理 * @param service * @return */ @Override public ComponentName startService(Intent service) { String className = getLoadServiceClassName(service); Intent intent = new Intent(this,ProxyService.class); intent.putExtra(Constants.SERVICE_CLASS_NAME,className); return super.startService(intent); }

总结

动态加载 Activity, Broadcast , Service 其实基本原理就是将插件中需要启动四大组件的信息告诉代理类中,让代理类来负责处理插件中的逻辑,代理类中处理完之后通过 IActivity, IBroadcast, IService 来通知插件。

动态加载插件我们这篇文章就讲到这里了,感兴趣的可以参考项目地址 ,这个实现方案不适合线上商业项目,使用需谨慎。如果项目中只用到了插件中的生命周期可以选择性的使用。

系列文章

移动架构 (一) 详解架构设计中UML图的使用

移动架构 (二) Android 中 Handler 架构分析,并实现自己简易版本 Handler 框架

移动架构 (三) AMS 源码分析

移动架构 (四) EventBus源码分析及实现自己的轻量级 EventBus 框架

移动架构 (五) 了解Android数据持久化

移动架构 (六) 轻量级进程间通信框架设计

移动架构 (七) 组件化架构设计思想

移动架构 (八) 动态化加载插件技术模型实现



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3